package models

import (
	"context"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base32"
	"time"

	"github.com/jmoiron/sqlx"
	"learning.com/edu/utils/validator"
)

// 令牌作用域
const (
	ScopeActivaction 	= "activaction" // 激活
	ScopeAuthentication = "authentication" // 认证
)

// 令牌结构体
type Token struct {
	PlainToken string `json:"token"` // 令牌
	Hash 	 []byte `json:"-"` // 令牌哈希值
	UserId 	   int64 `json:"-"` // 用户ID
	Expiry     time.Time `json:"expiry"` // 过期时间
	Scope      string `json:"-"` // 用途
}

// 验证令牌
func ValidateTokenPlainToken(v *validator.Validator, token string) {
	v.Check(token != "", "token", "must be provided")
	v.Check(len(token) == 26, "token", "must be 26 bytes long")
}

// 令牌模型
type TokenModel struct {
	DB *sqlx.DB
}

/**
 * 创建令牌
 */
func (m *TokenModel) New(userId int64, ttl time.Duration, scope string) (*Token, error) { 
	token, err := m.generateToken(userId, ttl, scope)
	if err != nil {
		return nil, err
	}

	// 插入数据库
	err = m.Insert(token)
	if err != nil {
		return nil, err
	} else {
		return token, nil
	}
}

/**
 * 生成令牌
 */
func (m *TokenModel) generateToken(userId int64, ttl time.Duration, scope string) (*Token, error) {
	token := &Token{
		UserId: userId,
		Expiry: time.Now().Add(ttl),
		Scope: scope,
	}
	
	// Generate a 32 byte random token
	radomBytes := make([]byte, 16)

	_, err := rand.Read(radomBytes)
	if err != nil {
		return nil, err
	}

	// 生成token
	token.PlainToken = base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(radomBytes)
	
	// 生成hash
	hash := sha256.Sum256([]byte(token.PlainToken))
	token.Hash = hash[:]

	return token, nil
}

/**
 * 保存token到数据库
 */
func (t *TokenModel) Insert(token *Token) error {
	query := `INSERT INTO appuser.tokens (hash, user_id, expiry, scope)
			  VALUES ($1, $2, $3, $4)`
	// 创建参数
	args := []any {
		token.Hash,
		token.UserId,
		token.Expiry,
		token.Scope,
	}

	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	// 执行SQL
	_, err := t.DB.ExecContext(ctx, query, args...)
	return err
}

/**
 * 删除用户相关的所有token
 */
 func (t *TokenModel) DeleteAllForUser(scope string, userId int64) error {
	query := `DELETE FROM appuser.tokens WHERE scope = $1 AND user_id = $2`
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	// 执行SQL
	_, err := t.DB.ExecContext(ctx, query, scope, userId)
	return err
}

/**
 * 删除超时的token
 */
 func (t *TokenModel) DeleteAllExpired() error {
	query := `DELETE FROM appuser.tokens WHERE expiry < $1`
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	// 执行SQL
	_, err := t.DB.ExecContext(ctx, query, time.Now())
	return err
}

/**
 * 删除特定的token
 */
 func (t *TokenModel) DeleteToken(token string) error {
	tokenHash := sha256.Sum256([]byte(token))

	query := `DELETE FROM appuser.tokens WHERE hash = $1`
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
	defer cancel()

	// 执行SQL
	_, err := t.DB.ExecContext(ctx, query, tokenHash[:])
	return err
}